#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <cerrno>
#include <cstring>
#include <memory>
#include <sys/epoll.h>
#include "util.hpp"
#include "easysock.hpp"
#include "easylog.hpp"
#include "epoll.hpp"

namespace server
{
struct connection;

const int         default_fd    = -1;
const std::string default_ip    = "127.0.0.1";
const uint16_t    default_port  = -1;

using service_t  = std::function<std::string(const std::string&)>;
using callback_t = std::function<void(connection&)>;

struct connection
{
    connection(int fd = default_fd, 
               event_type ev = epnil, 
               std::string ip = default_ip,
               uint16_t port = default_port)
        : _fd(fd)
        , _ev(ev)
        , _ip(ip)
        , _port(port) 
    {}

    void register_cb(callback_t recver, callback_t sender, callback_t excepter)
    {
        _recver   = recver;
        _sender   = sender;
        _excepter = excepter;
    }

    int _fd;
    event_type _ev;

    std::string _ip;
    uint16_t _port;

    std::string _ibuf;
    std::string _obuf;

    callback_t _recver;
    callback_t _sender;
    callback_t _excepter;
};

class epoll_server : public inet::tcp::server
{
public:
    epoll_server(uint16_t port, service_t service = nullptr)
        : server(port), _service(service)
    {}

    ~epoll_server()
    {
        for (auto& kv : _conns) { close(kv.first); }
    }

    void init()
    {
        _ep.create();
        add_conn(_sock, evin);
    }

    void dispatcher()
    {
        while (true)
        {
            handler();
        }
    }

    void handler()
    {
        int n = _ep.wait(-1);

        for (int i = 0; i < n; i++)
        {
            const uint32_t& events = _ep[i].events;
            const int& fd = _ep[i].data.fd;

            if (events & evin)  _conns[fd]._recver(_conns[fd]);
            if (events & evout) _conns[fd]._sender(_conns[fd]);
            if (events & everr) _conns[fd]._excepter(_conns[fd]);
        }
    }

private:
    void acceptor(connection& conn)
    {
        try 
        {
            std::string cip;
            uint16_t cport;
            int newfd = accept(&cip, &cport);

            add_conn(newfd, evin, cip, cport);
            INFO("accept connect %d -> [%s:%d]", newfd, cip.c_str(), cport);
        } 
        catch (const std::runtime_error& e)
        {
            return;
        }
    }

    void recver(connection& conn)
    {
        INFO("handler recv event on %d", conn._fd);

        std::string req;
        ssize_t s = recv(conn._fd, &req, 1024);

        if (s > 0)
        {
            conn._ibuf += req;
            conn._obuf += _service(conn._ibuf); 
        }
        else
        {
            if (s == 0) INFO("client quit");
            else WARN("recv error, %d %s", errno, strerror(errno));
            del_conn(conn._fd);
        }
    }

    void sender(connection& conn)
    {
        INFO("handler send event on %d", conn._fd);

        size_t s = send(conn._fd, conn._obuf);

        if (s <= 0)
        {
            if (s == 0) INFO("client quit");
            else WARN("send error, %d %s", errno, strerror(errno));
            del_conn(conn._fd);
        }
    }

    void excepter(connection& conn)
    {
        INFO("handler except event on %d", conn._fd);
    }

    void add_conn(int fd, event_type ev, const std::string& ip = default_ip, uint16_t port = default_port)
    {
        _conns.insert({fd, connection(fd, ev, ip, port)});

        if (fd == _sock)
        {
            _conns[fd].register_cb(std::bind(&epoll_server::acceptor, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            _conns[fd].register_cb(std::bind(&epoll_server::recver,   this, std::placeholders::_1), \
                                   std::bind(&epoll_server::sender,   this, std::placeholders::_1), \
                                   std::bind(&epoll_server::excepter, this, std::placeholders::_1));
        }

        if      (ev == evin)  _ep.add_event( fdev_in(fd));
        else if (ev == evio)  _ep.add_event( fdev_io(fd));
        else if (ev == evout) _ep.add_event(fdev_out(fd));
        else if (ev == everr) _ep.add_event(fdev_err(fd));
    }

    void del_conn(int fd)
    {
        _ep.del_event(fdev_in(fd)); // epoll_ctl要求fd必须合法，所以先移除事件再close fd
        _conns.erase(fd);
        close(fd);
    }

private:
    epoll _ep;
    service_t _service;
    std::unordered_map<int, connection> _conns;
};

}
